• 请在环境变量中设置DB_URI指向数据库

In [1]:
%matplotlib inline
import os
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from PyFin.api import *
from alphamind.api import *
from alphamind.strategy.strategy import Strategy, RunningSetting
from alphamind.portfolio.meanvariancebuilder import target_vol_builder

plt.style.use('ggplot')

1. Single Day Analysis



In [2]:
ref_date = '2018-01-08'
engine = SqlEngine(os.environ['DB_URI'])
universe = Universe('zz800')

In [3]:
codes = engine.fetch_codes(ref_date, universe)
total_data = engine.fetch_data(ref_date, 'EPS', codes, 906, industry='sw', risk_model='day')
all_styles = risk_styles + industry_styles + ['COUNTRY']

In [4]:
risk_cov = total_data['risk_cov'][all_styles].values
factor = total_data['factor']
risk_exposure = factor[all_styles].values
special_risk = factor['srisk'].values

Portfolio Construction

  • using EPS factor as alpha factor;
  • short selling is forbiden;
  • target of volatility for the activate weight is setting at 2.5% annually level.

In [5]:
er = factor['EPS'].values
bm = factor['weight'].values
lbound = np.zeros(len(er))
ubound = bm + 0.01
cons_mat = np.ones((len(er), 1))
risk_targets = (bm.sum(), bm.sum())
target_vol = 0.025
risk_model = dict(cov=None, factor_cov=risk_cov/10000, factor_loading=risk_exposure, idsync=special_risk ** 2 / 10000.)

status, p_er, p_weight = \
    target_vol_builder(er, risk_model, bm, lbound, ubound, cons_mat, risk_targets, target_vol)
    
sec_cov = risk_exposure @ risk_cov @ risk_exposure.T / 10000. + np.diag(special_risk ** 2) / 10000

In [6]:
# check the result
print(f"total weight is {p_weight.sum(): .4f}")
print(f"portfolio activate weight forecasting vol is {np.sqrt((p_weight - bm) @ sec_cov @ (p_weight - bm)):.4f}")
print(f"portfolio er: {p_weight @ er:.4f} comparing with benchmark er: {bm @ er:.4f}")


total weight is  1.0000
portfolio activate weight forecasting vol is 0.0250
portfolio er: 2.2232 comparing with benchmark er: 1.2359

2. Porfolio Construction: 2016 ~ 2018



In [7]:
"""
Back test parameter settings
"""

start_date = '2016-01-01'
end_date = '2018-02-08'

freq = '10b'
neutralized_risk = industry_styles
industry_name = 'sw_adj'
industry_level = 1
risk_model = 'short'
batch = 0
horizon = map_freq(freq)
universe = Universe('zz800')
data_source = os.environ['DB_URI']
benchmark_code = 906
target_vol = 0.05
weights_bandwidth = 0.02

In [8]:
"""
Factor Model
"""

alpha_factors = {'f01': CSRank(LAST('EPS'))}
weights = dict(f01=1.)
alpha_model = ConstLinearModel(features=alpha_factors, weights=weights)

data_meta = DataMeta(freq=freq,
                         universe=universe,
                         batch=batch,
                         neutralized_risk=neutralized_risk,
                         risk_model='short',
                         pre_process=[winsorize_normal, standardize],
                         post_process=[standardize],
                         warm_start=0,
                         data_source=data_source)

In [9]:
"""
Constraintes settings
"""

constraint_risk = ['SIZE', 'SIZENL', 'BETA']
total_risk_names = constraint_risk + ['benchmark', 'total']

b_type = []
l_val = []
u_val = []

previous_pos = pd.DataFrame()
rets = []
turn_overs = []
leverags = []

for name in total_risk_names:
    if name == 'benchmark':
        b_type.append(BoundaryType.RELATIVE)
        l_val.append(0.8)
        u_val.append(1.0)
    else:
        b_type.append(BoundaryType.ABSOLUTE)
        l_val.append(0.0)
        u_val.append(0.0)

bounds = create_box_bounds(total_risk_names, b_type, l_val, u_val)

In [10]:
"""
Running Settings
"""
running_setting = RunningSetting(weights_bandwidth=weights_bandwidth,
                                 rebalance_method='tv',
                                 bounds=bounds,
                                 target_vol=target_vol)

In [11]:
"""
Strategy run
"""
strategy = Strategy(alpha_model,
                    data_meta,
                    universe=universe,
                    start_date=start_date,
                    end_date=end_date,
                    freq=freq,
                    benchmark=benchmark_code)
strategy.prepare_backtest_data()
ret_df, positions = strategy.run(running_setting)


2019-02-10 00:37:10,009 - ALPHA_MIND - INFO - alpha factor data loading finished ...
2019-02-10 00:37:11,605 - ALPHA_MIND - INFO - industry data loading finished ...
2019-02-10 00:37:12,757 - ALPHA_MIND - INFO - benchmark data loading finished ...
2019-02-10 00:37:23,289 - ALPHA_MIND - INFO - risk_model data loading finished ...
2019-02-10 00:37:39,142 - ALPHA_MIND - INFO - returns data loading finished ...
2019-02-10 00:37:39,427 - ALPHA_MIND - INFO - starting backting ...
2019-02-10 00:37:39,521 - ALPHA_MIND - INFO - alpha models training finished ...
D:\ProgramData\anaconda3\lib\site-packages\pandas\core\frame.py:4024: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  downcast=downcast, **kwargs)
2019-02-10 00:37:39,662 - ALPHA_MIND - INFO - 2016-01-04 00:00:00 re-balance: 799 codes
2019-02-10 00:37:39,811 - ALPHA_MIND - INFO - 2016-01-18 00:00:00 re-balance: 800 codes
2019-02-10 00:37:39,981 - ALPHA_MIND - INFO - 2016-02-01 00:00:00 re-balance: 800 codes
2019-02-10 00:37:40,148 - ALPHA_MIND - INFO - 2016-02-22 00:00:00 re-balance: 800 codes
2019-02-10 00:37:40,314 - ALPHA_MIND - INFO - 2016-03-07 00:00:00 re-balance: 800 codes
2019-02-10 00:37:40,495 - ALPHA_MIND - INFO - 2016-03-21 00:00:00 re-balance: 800 codes
2019-02-10 00:37:40,670 - ALPHA_MIND - INFO - 2016-04-05 00:00:00 re-balance: 800 codes
2019-02-10 00:37:40,841 - ALPHA_MIND - INFO - 2016-04-19 00:00:00 re-balance: 800 codes
2019-02-10 00:37:40,987 - ALPHA_MIND - INFO - 2016-05-04 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,117 - ALPHA_MIND - INFO - 2016-05-18 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,243 - ALPHA_MIND - INFO - 2016-06-01 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,357 - ALPHA_MIND - INFO - 2016-06-17 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,480 - ALPHA_MIND - INFO - 2016-07-01 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,598 - ALPHA_MIND - INFO - 2016-07-15 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,715 - ALPHA_MIND - INFO - 2016-07-29 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,829 - ALPHA_MIND - INFO - 2016-08-12 00:00:00 re-balance: 800 codes
2019-02-10 00:37:41,951 - ALPHA_MIND - INFO - 2016-08-26 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,072 - ALPHA_MIND - INFO - 2016-09-09 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,189 - ALPHA_MIND - INFO - 2016-09-27 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,301 - ALPHA_MIND - INFO - 2016-10-18 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,416 - ALPHA_MIND - INFO - 2016-11-01 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,553 - ALPHA_MIND - INFO - 2016-11-15 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,710 - ALPHA_MIND - INFO - 2016-11-29 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,843 - ALPHA_MIND - INFO - 2016-12-13 00:00:00 re-balance: 800 codes
2019-02-10 00:37:42,967 - ALPHA_MIND - INFO - 2016-12-27 00:00:00 re-balance: 800 codes
2019-02-10 00:37:43,089 - ALPHA_MIND - INFO - 2017-01-11 00:00:00 re-balance: 800 codes
2019-02-10 00:37:43,222 - ALPHA_MIND - INFO - 2017-01-25 00:00:00 re-balance: 799 codes
2019-02-10 00:37:43,341 - ALPHA_MIND - INFO - 2017-02-15 00:00:00 re-balance: 799 codes
2019-02-10 00:37:43,466 - ALPHA_MIND - INFO - 2017-03-01 00:00:00 re-balance: 800 codes
2019-02-10 00:37:43,589 - ALPHA_MIND - INFO - 2017-03-15 00:00:00 re-balance: 800 codes
2019-02-10 00:37:43,719 - ALPHA_MIND - INFO - 2017-03-29 00:00:00 re-balance: 800 codes
2019-02-10 00:37:43,842 - ALPHA_MIND - INFO - 2017-04-14 00:00:00 re-balance: 800 codes
2019-02-10 00:37:43,958 - ALPHA_MIND - INFO - 2017-04-28 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,082 - ALPHA_MIND - INFO - 2017-05-15 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,212 - ALPHA_MIND - INFO - 2017-05-31 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,337 - ALPHA_MIND - INFO - 2017-06-14 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,457 - ALPHA_MIND - INFO - 2017-06-28 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,580 - ALPHA_MIND - INFO - 2017-07-12 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,703 - ALPHA_MIND - INFO - 2017-07-26 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,835 - ALPHA_MIND - INFO - 2017-08-09 00:00:00 re-balance: 800 codes
2019-02-10 00:37:44,939 - ALPHA_MIND - INFO - 2017-08-23 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,059 - ALPHA_MIND - INFO - 2017-09-06 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,175 - ALPHA_MIND - INFO - 2017-09-20 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,298 - ALPHA_MIND - INFO - 2017-10-11 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,417 - ALPHA_MIND - INFO - 2017-10-25 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,539 - ALPHA_MIND - INFO - 2017-11-08 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,686 - ALPHA_MIND - INFO - 2017-11-22 00:00:00 re-balance: 800 codes
2019-02-10 00:37:45,842 - ALPHA_MIND - INFO - 2017-12-06 00:00:00 re-balance: 800 codes
2019-02-10 00:37:46,005 - ALPHA_MIND - INFO - 2017-12-20 00:00:00 re-balance: 800 codes
2019-02-10 00:37:46,150 - ALPHA_MIND - INFO - 2018-01-04 00:00:00 re-balance: 800 codes
2019-02-10 00:37:46,295 - ALPHA_MIND - INFO - 2018-01-18 00:00:00 re-balance: 800 codes
2019-02-10 00:37:46,464 - ALPHA_MIND - INFO - 2018-02-01 00:00:00 re-balance: 800 codes

In [12]:
ret_df[['excess_return', 'turn_over']].cumsum().plot(figsize=(14, 7),
                                                     title='Fixed freq rebalanced with target vol \
                                                     at {2}: {0} with benchmark {1}'.format(freq, benchmark_code, target_vol),
                                                     secondary_y='turn_over')


Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x192ff3d0eb8>

In [ ]: